#include <stdio.h>

#include "World Module.h"
#include "World Includes.h"
#include "Engine\Engine.h"
#include "Item\ItemFactory.h"
#include "Item\Items.h"

class Engine;
#define projList (TemplateVectorList<Item_Projectile>*)

int const _World::InitializeNew(bool Overwrite) {
	return InitializeNewExB( MAP_FOLDER_NAME, BASE_MAP_NAME, KILOMETER, KILOMETER, Overwrite);
}

int const _World::InitializeNewExA( int ChunkAmountH, int ChunkAmountW, bool Overwrite) {
	return InitializeNewExB( MAP_FOLDER_NAME, BASE_MAP_NAME, ChunkAmountH, ChunkAmountW, Overwrite);
}

/** Creates and generates a new world */
int const _World::InitializeNewExB( char * a_MainSaveDir, char * a_SaveDir, int ChunkAmountH, int ChunkAmountW, bool Overwrite) {
	int i = 0;

	MapH = ChunkAmountH;
	MapW = ChunkAmountW;

	if(WorldSeed == 0) WorldSeed = (unsigned int)time(0);

	srand(WorldSeed);

	Density = (rand() % 3) + 1;
	TotalCount = Density * MapW;

	// create a temp entity of each race because of inefficient code
	Entity* e = new Entity;
	e->loadCharacter("Resources/Stats/CQCDrone.txt", 1);
	e->destroyCharacter();
	e->loadCharacter("Resources/Stats/RangedDrone.txt", 1);
	e->destroyCharacter();
	delete e;

	InitPlayer();
	InitEntities(Density,TotalCount);
	animationEngine.init();


	//A 2D array
	Map = new _Chunk[MapH * MapW];
	if(!Map) return FAIL_MEMORY_ALLOCATION;
	//An array of 2D arrays
	// 0 1 2
	// 3 4 5
	// 6 7 8
	//This memory will be passed to the chunks that need to load
	// the chunk will put its data into this slot so it does not
	// need to keep allocating and deleting memory
	for(i = 9;i >= 0;--i) {
		BufferSpace[i] = new Block[CHUNK_HEIGHT * CHUNK_WIDTH];
	}

	//Starts up the generator
	GENERATOR::Ins()->Initialize(&Map,MapH,MapW);

	// Creates the directory for the map
	// If the directory already exists and its not allowed to overwrite, it adds a new one with an incremented number on the end
	SetupDirectory(a_MainSaveDir,a_SaveDir,Overwrite);

	SetupNewSaveFile();

	Result(LoadAssets());

	//Setup the 9 chunks to be loaded first
	Result(SetupGameplay());
	AddDrawList();

	SetupEntities(Density);

	//Reloads the area around the first player
	ForceReload((float)Players[LOCAL_PLAYER]->getPos().x,(float)Players[LOCAL_PLAYER]->getPos().y,0.0f,true);

	//Do a save to make sure that the world can be loaded again if something fails later on
	Result(Save());

	return SUCCEED;
}

int const _World::InitializeExisting( char * a_MainSaveDir, char* a_SaveDir, int ChunkAmountH, int ChunkAmountW)
{
	/*if(!rawMultiSaveData)
		return FAIL_FILE_NOT_AVAILABLE;*/

	if(MainSavePath == NULL)
		MainSavePath = MAP_FOLDER_NAME;
	if(MySavePath == NULL)
		MySavePath = BASE_MAP_NAME;

	FullPath = GetPathDir();

	MapH = ChunkAmountH;
	MapW = ChunkAmountW;

	// create a temp entity of each race because of inefficient code
	Entity* e = new Entity;
	e->loadCharacter("Resources/Stats/CQCDrone.txt", 1);
	e->destroyCharacter();
	e->loadCharacter("Resources/Stats/RangedDrone.txt", 1);
	e->destroyCharacter();
	delete e;

	// de-concatenate world data into proper files
	static ofstream outfile;
	char buffer[128];
	int i = 0;
	if( rawMultiSaveData ) {
		for(int y = 0; y < MapH; ++y)
		{
			for(int x = 0; x < MapW; ++x, ++i)
			{
				char* temp = rawMultiSaveData[i];
				sprintf_s(buffer,128,"%s%i %i.txt",FullPath,y,x);
				outfile.open(buffer);
				if(outfile.is_open())
					outfile << rawMultiSaveData[i];
				outfile.close();
			}
		}
		InitPlayer();
		InitServerEntities();
	}
	else {
		InitPlayer();
		Density = (rand() % 3) + 1;
		TotalCount = Density * MapW;
		InitEntities(Density,TotalCount);
	}
	
	
	animationEngine.init();
	//A 2D array
	Map = new _Chunk[MapH * MapW];
	if(!Map) return FAIL_MEMORY_ALLOCATION;
	//An array of 2D arrays
	// 0 1 2
	// 3 4 5
	// 6 7 8
	//This memory will be passed to the chunks that need to load
	// the chunk will put its data into this slot so it does not
	// need to keep allocating and deleting memory
	for(i = 9;i >= 0;--i) {
		BufferSpace[i] = new Block[CHUNK_HEIGHT * CHUNK_WIDTH];
	}

	if(WorldSeed == 0) WorldSeed = (unsigned int)time(0);
	srand(WorldSeed);

	GENERATOR::Ins()->Initialize(&Map,MapH,MapW);

	//Checks if the Directory exists. If it doesn't, it makes the directory.
	// and does the normal setup method
	if(!VerifyDirectory(a_MainSaveDir,a_SaveDir)) {
		SetupNewSaveFile();
	}
	else {
		SetupExistingSaveFile();
	}

	Result(LoadAssets());
	
	//Setup the 9 chunks to be loaded first
	Result(SetupGameplay());
	AddDrawList();

	if(Density) {
		SetupEntities(Density);
	}

	//Reloads the area around the first player
	ForceReload((float)Players[LOCAL_PLAYER]->getPos().x,(float)Players[LOCAL_PLAYER]->getPos().y,0.0f,true);

	//Do a save to make sure that the world can be loaded again if something fails later on
	Result(Save());

	return SUCCEED;
}

int const _World::SetupGameplay() {
	for(int y = 0;y < MapH;y++) {
		for(int x = 0;x < MapW;x++) {
			Map[x + y * MapH].Load(BufferSpace[0]);
			CurrentLoc[0] = &Map[x + y * MapH];
			SetAssets(CurrentLoc[0]);
			CurrentLoc[0]->Unload();
		}
	}

	int px,py;
	int x = 0;
	int y = 0;//MapW / 2 - 1,y = MapH / 2 - 1;
	for(int i = 0;i < 9;i++) {
		Map[x + (y * MapH)].Load(BufferSpace[i]);
		CurrentLoc[i] = &Map[x + (y * MapH)];
		//SetAssets(CurrentLoc[i]);
		//CurrentLoc[i]->Save();
		if(++x > ((MapW / 2 - 1) + 2)) {
			x = MapW / 2 - 1;
			y++;
		}
	}

	for(int i = 0;i < MAX_LIST_SIZE;i++) {
		DrawList[i] = new Block_Info();
		CollideList[i] = new Block_Info();
	}

	//Minus one to certain variables because they are in base 1 while an array is in base 0
	// 4 is the center of a 2D array of 9
	float H, W = 0;
	Players[LOCAL_PLAYER]->CalcBBDimensions(H,W);
	//!//Changed pos to the end
	px = BLOCK_TEXTURE_SIZE;
	//px = (MapW-1) * CHUNK_WIDTH * BLOCK_TEXTURE_SIZE - BLOCK_TEXTURE_SIZE;
	//Calculates his y position based off of the nearest ground block at his x position
	py = FindGroundLevel(px,true) * -1 + (int)H;
	px += (int)W * 3;
	Players[LOCAL_PLAYER]->SetPos(Vector3D(px,py,0));
	
	return SUCCEED;
}

int const _World::AddDrawList() {
	Engine::Ins()->dlHandler.AddWorldBlocks(DrawList,MAX_LIST_SIZE);
	return SUCCEED;
}

int const _World::InitPlayer() {
	for(int i = 0; i < MAX_PLAYERS; ++i)
		Players[i] = NULL;
	//PLAYER
	Players[LOCAL_PLAYER] = new Entity;
	Players[LOCAL_PLAYER]->loadCharacter("Resources/Stats/Debug Stat Sheet.txt", P_TYPE_PLAYER);
	Players[LOCAL_PLAYER]->objectType = TYPE_PLAYER_PIECE;

	Players[LOCAL_PLAYER]->WipeActionList();
	AllEntities->push(Players[LOCAL_PLAYER]);
	return SUCCEED;
}

int const _World::InitEntities(int Density,int TotalCount) {
	Entity * NewEntity = NULL;
	for(int i = 0;i < TotalCount;i++) {
		NewEntity = new Entity;
		NewEntity->WipeActionList();
		int type = rand() % 2;
		char* filename;
		if(type == 0)
			filename = "Resources/Stats/CQCDrone.txt";
		else if(type == 1)
			filename = "Resources/Stats/RangedDrone.txt";
		NewEntity->loadCharacter(filename, P_TYPE_ENEMY);

		NewEntity->uniqueID = AllEntities->push(NewEntity);

	}
	NewEntity = NULL;

	return SUCCEED;
}

int const _World::InitServerEntities() {
	
	return SUCCEED;
}

int const _World::SetupEntities(int Density) {
	int CurWPos = 0;
	int x = 0,y = 0;
	float H, W = 0;
	Players[LOCAL_PLAYER]->CalcBBDimensions(H,W);
	for(int i = 1,d = 0;i < AllEntities->size();i++,d++) {
		//if(!AllEntities->getByIndex(i)->isAlive())
			AllEntities->getByIndex(i)->resetPlayerfromDeath();
		x = CurWPos * CHUNK_WIDTH * BLOCK_TEXTURE_SIZE;
		x += (rand() % (CHUNK_WIDTH * BLOCK_TEXTURE_SIZE)) + 1;
		if(CurWPos == 0 && x <= (15 * BLOCK_TEXTURE_SIZE)) {
			x = BLOCK_TEXTURE_SIZE * 15;
		}
		else if(CurWPos == 19 && x >= ((CHUNK_WIDTH - 2) * BLOCK_TEXTURE_SIZE)) {
			x = (CHUNK_WIDTH - 2) * BLOCK_TEXTURE_SIZE;
		}
		y = FindGroundLevel(x,true) * -1 + (int)H * 2;

		x += (int)W * 2;
		AllEntities->getByIndex(i)->SetPos((float)x + (int)W,(float)y,AllEntities->getByIndex(0)->getPos().z);
		if(d + 1 >= Density) {
			d = -1;
			CurWPos++;
			if(CurWPos >= MapW-1)
				CurWPos--;
		}
	}
	return SUCCEED;
}

Entity* _World::getEntity(unsigned i) 
{
	if(i < (unsigned)AllEntities->size()) 
		return AllEntities->getByIndex(i); 
	else return NULL; 
}

Entity* _World::getEntityByID(unsigned i)
{
	if(i < (unsigned)AllEntities->size())
	{
		for(int i = 0; i < AllEntities->size(); ++i)
		{
			if(AllEntities->getByIndex(i)->uniqueID == i)
				return AllEntities->getByIndex(i);
		}
	}
	
	return NULL;
}

int _World::getNumEntities() 
{ return AllEntities->size(); }

int const _World::ResetEntities() {
	Entity * tEntity;
	int x = 0,y = 0;
	float H,W;
	Players[LOCAL_PLAYER]->CalcBBDimensions(W,H);
	for(int i = 1;i <= AllEntities->size();i++) {
		tEntity = AllEntities->getByIndex(i);
		tEntity->resetPlayerfromDeath();
		x = 0;
		y = FindGroundLevel(x,false) * -1 + (int)H * 3;
		tEntity->SetPos((float)x,(float)y,0);
		x = y = 0;
	}
	tEntity = NULL;

	return SUCCEED;
}

int const _World::SetupNewTextFiles(std::ofstream & out,int ChunkAmountH, int ChunkAmountW) {
	//Creates all the chunks files and initializes them

	// TODO : Check for existing files before attempting to create them
	CreateDirectory(MAP_FOLDER_NAME"\\"BASE_MAP_NAME,NULL);
	char * Buffer = NULL, * Buffer2 = new char[64];
	for(int y = MapH - 1,x = MapW - 1;y >= 0;x--) {

		//Creates the file for the chunk to load from and save to
		/* Buffer = <Map Folder Name>\<World Name>\X Y.txt */
		Buffer = new char[128];
		sprintf_s(Buffer,128,"%s%i %i.txt",FullPath,x,y);
		CreateFile(Buffer,NULL,NULL,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);

		//Writes base information to the file that the chunk will load from
		out.open(Buffer,std::ios::out);
		//Half the world is dirt, the rest is just air
		out.write( " 20    ", 7);
		if( y > MapH / 2 )
			sprintf_s(Buffer2,64," 2500 0 %i %i %i",BLOCK_TYPE_DIRT >> 8,BLOCK_TRAIT_WEIGHT >> 16,BLOCK_TRAIT_POW_POWER1 >> 24);
		else if( y == MapH / 2)
			sprintf_s(Buffer2,64," 1250 0 %i %i %i 1250 0 %i %i %i",BLOCK_TYPE_DIRT >> 8,BLOCK_TRAIT_WEIGHT >> 16,BLOCK_TRAIT_POW_POWER1 >> 24,BLOCK_TYPE_AIR >> 8,BLOCK_TRAIT_DIRDOWN >> 16,BLOCK_TRAIT_POW_POWER1 >> 24);
		else
			sprintf_s(Buffer2,64," 2500 0 %i %i %i",BLOCK_TYPE_AIR >> 8,BLOCK_TRAIT_DIRDOWN >> 16,BLOCK_TRAIT_POW_POWER1 >> 24);
		out << Buffer2;
		out.close();


		//Creates the chunk, does not load the information in its file into memory at this time
		// SUCCEED == 0 therefore this statement will call break; if it fails
		if(Map[x + (y * ChunkAmountH)].Setup(Buffer,NULL,x,y,CHUNK_HEIGHT,CHUNK_WIDTH))
		{break;}

		if( x <= 0 ) {
			x = MapW;
			--y;
		}
	}
	delete Buffer2;
	return SUCCEED;
}

int const _World::ResetTextFiles(std::ofstream & out,int ChunkAmountH, int ChunkAmountW) {
	char * Buffer = NULL, * Buffer2 = new char[64];
	for(int y = MapH - 1,x = MapW - 1;y >= 0;x--) {

		//Creates the file for the chunk to load from and save to
		/* Buffer = <Map Folder Name>\<World Name>\X Y.txt */
		Buffer = new char[128];
		sprintf_s(Buffer,128,"%s%i %i.txt",FullPath,x,y);

		//Writes base information to the file that the chunk will load from
		out.open(Buffer,std::ios::out);
		//Half the world is dirt, the rest is just air
		out.write( " 20    ", 7);
		if( y > MapH / 2 )
			sprintf_s(Buffer2,64," 2500 0 %i %i %i",BLOCK_TYPE_DIRT >> 8,BLOCK_TRAIT_WEIGHT >> 16,BLOCK_TRAIT_POW_POWER1 >> 24);
		else if( y == MapH / 2)
			sprintf_s(Buffer2,64," 1250 0 %i %i %i 1250 0 %i %i %i",BLOCK_TYPE_DIRT >> 8,BLOCK_TRAIT_WEIGHT >> 16,BLOCK_TRAIT_POW_POWER1 >> 24,BLOCK_TYPE_AIR >> 8,BLOCK_TRAIT_DIRDOWN >> 16,BLOCK_TRAIT_POW_POWER1 >> 24);
		else
			sprintf_s(Buffer2,64," 2500 0 %i %i %i",BLOCK_TYPE_AIR >> 8,BLOCK_TRAIT_DIRDOWN >> 16,BLOCK_TRAIT_POW_POWER1 >> 24);
		out << Buffer2;
		out.close();

		if( x <= 0 ) {
			x = MapW;
			--y;
		}
	}
	delete Buffer2;
	return SUCCEED;
}

int const _World::SetupNewSaveFile() {
	std::ifstream in;
	std::ofstream out;
	
	//Initializes the text files with basic data
	SetupNewTextFiles(out,MapH,MapW);

	//Do all generation logic
	GENERATOR::Ins()->Generate();

	GENERATOR::Ins()->CleanUp();

	return SUCCEED;
}

int const _World::ResetSaveFile() {
	std::ifstream in;
	std::ofstream out;

	//Initializes the text files with basic data
	ResetTextFiles(out,MapH,MapW);

	//Do all generation logic
	GENERATOR::Ins()->Generate();

	GENERATOR::Ins()->CleanUp();

	return SUCCEED;
}

int const _World::SetupExistingSaveFile() {
	GENERATOR::Ins()->CleanUp();
	char * Buffer = NULL;
	for(int y = MapH - 1,x = MapW - 1;y >= 0;x--) {
		Buffer = new char[128];
		sprintf_s(Buffer,128,"%s%i %i.txt",FullPath,x,y);
		Map[x + (y * MapH)].Setup(Buffer,NULL,x,y,CHUNK_HEIGHT,CHUNK_WIDTH);

		if( x <= 0 ) {
			x = MapW;
			--y;
		}
	}

	return SUCCEED;
}

int const _World::SetupExistingBiomes(std::ifstream & in) {

	return SUCCEED;
}

int const _World::SetupDirectory( char * MainDir , char * a_SaveDir , bool a_Overwrite ) {
	CreateDirectory(MainDir,NULL);
	MainSavePath = new char[64];
	MainSavePath = MainDir;
	MySavePath = new char[64];
	MySavePath = a_SaveDir;
	if(FullPath) delete FullPath;
	FullPath = new char[128];
	//Save the last error so I can give it back to the operating system after I finish creating the directory
	DWORD temp = GetLastError();

	//Clear out the current errors so I can get a clean message from windows
	SetLastError(0);
	sprintf_s(FullPath,128,"%s\\%s\\",MainDir,a_SaveDir);
	CreateDirectory(FullPath,NULL);
	if( !a_Overwrite ) {
		int i = 0;
		while( GetLastError() == ERROR_ALREADY_EXISTS ) {
			/* FullPath = Name\Name#\ */
			sprintf_s(FullPath,128,"%s\\%s%i\\",MainDir,a_SaveDir,i++);
			CreateDirectory(FullPath,NULL);
		}
	}
	SetLastError(temp);
	return SUCCEED;
}

bool const _World::VerifyDirectory( char * MainDir, char * a_SaveDir ) {
	//Saves the FullPath to the directory
	if(!MainDir) MainDir = MAP_FOLDER_NAME;
	if(!a_SaveDir) a_SaveDir = BASE_MAP_NAME;

	FullPath = GetPathDir();
	//sprintf_s(FullPath,128,"%s\\%s",MainDir,a_SaveDir);

	//If it doesn't exist, call setupdiretory
	if(GetFileAttributes(FullPath) == INVALID_FILE_ATTRIBUTES) {
		SetupDirectory(MainDir,a_SaveDir,true);
		return false;
	}
	sprintf_s(FullPath,128,"%s\\%s\\",MainDir,a_SaveDir);
	return true;
}

int const _World::LoadAssets() {
	//TODO BY : ADD the other block types
	AssetIDLink * AssetID = new AssetIDLink;
	countToTen = 0;
	Vector2D * IconAsset = new Vector2D;

	// Read from a file to get all the blocks loaded
	//AssetID->ID = (unsigned int)Engine::Ins()->graphics.AddAsset("Resources/Meshes/size13.X","Resources/Sprites/Alpha.png", NULL, NULL,ASSET_WORLD).y;
	//AssetID->Type.SetType_s(BLOCK_TYPE_AIR);
	//AllAssets.push(*AssetID);
	////Load up all the icons in a parallel vector so they can be accessed by the GUI
	//IconAsset = &Engine::Ins()->graphics.AddAsset("Resources/Sprites/Alpha.png",NULL,NULL, ASSET_GUI);
	//AllIcons.push(*IconAsset);

	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/Human/Head.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/torso.x"					, "Resources/Sprites/Entity/Human/Body.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/Human/Arm.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/Human/Hand.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/Human/Thigh.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/Human/Foot.png", NULL, NULL, ASSET_WORLD);

	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/Zombie/Head.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/torso.x"					, "Resources/Sprites/Entity/Zombie/Body.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/Zombie/Arm.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/Zombie/Hand.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/Zombie/Thigh.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/Zombie/Foot.png", NULL, NULL, ASSET_WORLD);

	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/CQCDrone/Head.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/torso.x"					, "Resources/Sprites/Entity/CQCDrone/Body.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/CQCDrone/Arm.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/CQCDrone/Hand.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/CQCDrone/Thigh.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/CQCDrone/Foot.png", NULL, NULL, ASSET_WORLD);

	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/RangedDrone/Head.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/torso.x"					, "Resources/Sprites/Entity/RangedDrone/Body.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/RangedDrone/Arm.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/RangedDrone/Hand.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/RangedDrone/Thigh.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x"			, "Resources/Sprites/Entity/RangedDrone/Foot.png", NULL, NULL, ASSET_WORLD);

	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x" , "Resources/Sprites/Entity/Human/Deadhead.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x" , "Resources/Sprites/Entity/Zombie/Deadhead.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/character mesh.x" , "Resources/Sprites/Entity/CQCDrone/DeadHead.png", NULL, NULL, ASSET_WORLD);
	Engine::Ins()->graphics.AddAsset("Resources/Meshes/torso.x"			, "Resources/Sprites/Entity/CQCDrone/DeadBody.png", NULL, NULL, ASSET_WORLD);

	AssetID->ID = (unsigned int)Engine::Ins()->graphics.AddAsset("Resources/Meshes/size13.X","Resources/Sprites/Blocks/Dirt64.png", NULL, NULL,ASSET_WORLD).y;
	AssetID->Type.SetType_s(BLOCK_TYPE_DIRT);
	AllAssets.push(*AssetID);
	IconAsset = &Engine::Ins()->graphics.AddAsset("Resources/Sprites/Blocks/Dirt64.png",NULL,NULL, ASSET_GUI);
	AllIcons.push(*IconAsset);

	AssetID->ID = (unsigned int)Engine::Ins()->graphics.AddAsset("Resources/Meshes/size13.X","Resources/Sprites/Blocks/Grass64.png", NULL, NULL,ASSET_WORLD).y;
	AssetID->Type.SetType_s(BLOCK_TYPE_GRASS);
	AllAssets.push(*AssetID);
	IconAsset = &Engine::Ins()->graphics.AddAsset("Resources/Sprites/Blocks/Grass64.png",NULL,NULL, ASSET_GUI);
	AllIcons.push(*IconAsset);

	AssetID->ID = (unsigned int)Engine::Ins()->graphics.AddAsset("Resources/Meshes/size13.X","Resources/Sprites/Blocks/Rock64.png", NULL, NULL,ASSET_WORLD).y;
	AssetID->Type.SetType_s(BLOCK_TYPE_ROCK);
	AllAssets.push(*AssetID);
	IconAsset = &Engine::Ins()->graphics.AddAsset("Resources/Sprites/Blocks/Rock64.png",NULL,NULL, ASSET_GUI);
	AllIcons.push(*IconAsset);

	AssetID->ID = (unsigned int)Engine::Ins()->graphics.AddAsset("Resources/Meshes/size13.X","Resources/Sprites/Blocks/Tree64.png", NULL, NULL,ASSET_WORLD).y;
	AssetID->Type.SetType_s(BLOCK_TYPE_TREE);
	AllAssets.push(*AssetID);
	IconAsset = &Engine::Ins()->graphics.AddAsset("Resources/Sprites/Blocks/Tree64.png",NULL,NULL, ASSET_GUI);
	AllIcons.push(*IconAsset);

	AssetID->ID = (unsigned int)Engine::Ins()->graphics.AddAsset("Resources/Meshes/size13.X","Resources/Sprites/Blocks/Leaves64.png", NULL, NULL,ASSET_WORLD).y;
	AssetID->Type.SetType_s(BLOCK_TYPE_LEAF);
	AllAssets.push(*AssetID);
	IconAsset = &Engine::Ins()->graphics.AddAsset("Resources/Sprites/Blocks/Leaves64.png",NULL,NULL, ASSET_GUI);
	AllIcons.push(*IconAsset);

	AssetID->ID = (unsigned int)Engine::Ins()->graphics.AddAsset("Resources/Meshes/size13.X","Resources/Sprites/Blocks/Ore164.png", NULL, NULL,ASSET_WORLD).y;
	AssetID->Type.SetType_s(BLOCK_TYPE_ORE1);
	AllAssets.push(*AssetID);
	IconAsset = &Engine::Ins()->graphics.AddAsset("Resources/Sprites/Blocks/Ore164.png",NULL,NULL, ASSET_GUI);
	AllIcons.push(*IconAsset);

	AssetID->ID = (unsigned int)Engine::Ins()->graphics.AddAsset("Resources/Meshes/size13.X","Resources/Sprites/Blocks/Limiter64.png", NULL, NULL,ASSET_WORLD).y;
	AssetID->Type.SetType_s(BLOCK_TYPE_LIMITER);
	AllAssets.push(*AssetID);
	IconAsset = &Engine::Ins()->graphics.AddAsset("Resources/Sprites/Blocks/Limiter64.png",NULL,NULL, ASSET_GUI);
	AllIcons.push(*IconAsset);

	AssetID->ID = (unsigned int)Engine::Ins()->graphics.AddAsset("Resources/Meshes/size13.X","Resources/Sprites/Blocks/Win.png", NULL, NULL,ASSET_WORLD).y;
	AssetID->Type.SetType_s(BLOCK_TYPE_WIN);
	AllAssets.push(*AssetID);
	IconAsset = &Engine::Ins()->graphics.AddAsset("Resources/Sprites/Blocks/Win.png",NULL,NULL, ASSET_GUI);
	AllIcons.push(*IconAsset);



	delete AssetID;	
	return SUCCEED;
}

int const _World::SetAssets(_Chunk * C) {
	int y = 0,x = 0,arrPos = 0;
	Block * G = C->Access();
	AssetIDLink AssetID;
	if(!G) return FAIL_BAD_MEMORY_ACCESS;

	//Runs through every block in the chunk
	for(y = CHUNK_HEIGHT-1,x = CHUNK_WIDTH-1;y >= 0;--x) {
		//The position of the block in the array
		arrPos = x + (y * CHUNK_HEIGHT);

		// If the current block is able to be drawn
		if( G[arrPos].GetType_s() != BLOCK_TYPE_AIR ) {

			//Run through all the assets and see if the block matches any of them
			for(int i = AllAssets.size() - 1;i >= 0;--i) {
				AssetID = AllAssets.getByIndex(i);
				if( G[arrPos].GetType_s() == AssetID.Type.GetType_s() ) {
					G[arrPos].SetAssetID_s(AssetID.ID);
				}
			}
		}

		if( x <= 0 ) {
			x = CHUNK_WIDTH;
			y--;
		}
	}

	return SUCCEED;
}

_Chunk * _World::DetermineChunkLoc(float X,float Y, float Z) {
	_Chunk * cursor = CurrentLoc[4];
#ifdef _DEBUG
	assert(cursor != NULL);
#else
	if(!cursor) return NULL;
#endif
	
	//The current chunks position
	int xWorldPos = cursor->GetPosModX();
	int yWorldPos = cursor->GetPosModY();

	if ( (X < xWorldPos || X > xWorldPos + CHUNK_SIZE_W) ||
		(Y > -1 * yWorldPos || Y < -1 * yWorldPos - CHUNK_SIZE_H)) {
			//The new current chunks position
			int nextXWorldPos = 0;
			int nextYWorldPos = 0;

			nextXWorldPos = (int)(X / (CHUNK_SIZE_W));
			if(nextXWorldPos >= MapW) 
				nextXWorldPos = MapW-1;
			else if(nextXWorldPos < 0)
				nextXWorldPos = 0;

			nextYWorldPos = (int)(-1 * Y / (CHUNK_SIZE_H));
			if(nextYWorldPos >= MapH)
				nextYWorldPos = MapH-1;
			else if(nextYWorldPos < 0)
				nextYWorldPos = 0;

			return &Map[nextXWorldPos + (nextYWorldPos * MapH)];
	}
	return NULL;
}

int const _World::ReloadChunks(float X,float Y,float Z) {
	_Chunk * C = DetermineChunkLoc(X,Y,Z);
	if(C) {
		for(int i = 9-1;i >= 0;--i) {
			if( CurrentLoc[i] )
				if( CurrentLoc[i]->NeedSave() ) {
					CurrentLoc[i]->Unload();
					CurrentLoc[i]->Saved();
				}
				else 
					CurrentLoc[i]->Load(NULL);
			CurrentLoc[i] = NULL;
		}
		for(int i = 9-1,x = -1,y = -1;i >= 0;--i,x++) {
			if( C->GetX() + x < MapW && C->GetX() + x >= 0 &&
				C->GetY() + y < MapH && C->GetY() + y >= 0 ) {
					CurrentLoc[i] = &Map[C->GetX() + x + ((C->GetY() + y) * MapH)];
					CurrentLoc[i]->Load(BufferSpace[i]);
			}
			if ( x >= 1 ) {
				x = -2;
				y++;
			}
		}
	}
	return SUCCEED;
}

int const _World::ForceReload(float X,float Y,float Z,bool Save) {
	//The current center chunk
	_Chunk * C = NULL;

	//The new current chunks position
	int XWorldPos = 0;
	int YWorldPos = 0;

	XWorldPos = (int)(X / (CHUNK_SIZE_W));
	if(XWorldPos >= MapW) 
		XWorldPos = MapW-1;
	else if(XWorldPos < 0)
		XWorldPos = 0;

	YWorldPos = (int)(-1 * Y / (CHUNK_SIZE_H));
	if(YWorldPos >= MapH)
		YWorldPos = MapH-1;
	else if(YWorldPos < 0)
		YWorldPos = 0;


	C = &Map[XWorldPos + (YWorldPos * MapH)];
	if(C) {
		for(int i = 9-1;i >= 0;--i) {
			if(CurrentLoc[i] && Save) {
				CurrentLoc[i]->Unload();
				CurrentLoc[i]->Saved();
			}
			CurrentLoc[i] = NULL;
		}
		for(int i = 9-1,x = -1,y = -1;i >= 0;--i,x++) {
			if( C->GetX() + x < MapW && C->GetX() + x >= 0 &&
				C->GetY() + y < MapH && C->GetY() + y >= 0 ) {
					CurrentLoc[i] = &Map[C->GetX() + x + ((C->GetY() + y) * MapH)];
					CurrentLoc[i]->Load(BufferSpace[i]);
					if(!CurrentLoc[i]->Access()[0].GetAssetID_s())
						SetAssets(CurrentLoc[i]);
			}
			if ( x >= 1 ) {
				x = -2;
				y++;
			}
		}
	}
	return SUCCEED;
}

bool _World::DetermineEntityChunkLoc(float X,float Y,float Z) {
	//This is the central chunk, the one that the character is located in
	_Chunk * cursor = CurrentLoc[4];
	int xChunkWorldPos = cursor->GetPosModX();
	int yChunkWorldPos = cursor->GetPosModY();

	//This is the entire chunks range plus half a chunk to make sure that the enemies update as much as possible without breaking the game

	//CHUNK_SIZE_W = 500
	//left border = XChunkPos - 250
	//				(ChunkStartingX) - (Buffer Distance)
	//right border = XChunkPos + 500 + 250
	//              (ChunkX) + (WIDTH) + (Buffer distance)
	if ( (X > xChunkWorldPos - (CHUNK_SIZE_W/2) &&
		X < xChunkWorldPos + CHUNK_SIZE_W + (CHUNK_SIZE_W/2))
		&&
		//CHUNK_SIZE_H = 500
		//top border = YChunkPos - 250
		//			   (StartingY) - (Buffer)
		//bottom border =  YChunkPos + 500 + 250
		//				(StartingY) + (HEIGHT) + (BUFFER)
		//These are then multiplied by -1 to translate them into world coordinates
		(Y < -1 * (yChunkWorldPos - (CHUNK_SIZE_H/2)) &&
		(Y > -1 * (yChunkWorldPos + CHUNK_SIZE_H + (CHUNK_SIZE_H/2)))))
	{
		return true;
	}
	return false;
}

int const _World::ProcessChanges() {
	////The chunk with the current highest amount of changes
	//_Chunk * Priority = NULL;
	////Used to hold conversion numbers
	//int tx,ty;
	//WorldChange * cursor;
	////If this is set to true, it means some of the changes occured within loaded memory
	//// and the other set of logic needs to be executed
	//bool LoadedLogic = false;

	//for(int i = AllChanges.size()-1;
	//	i >= 0,!LoadedLogic;
	//	i--) {
	//	cursor = AllChanges.getByIndex(i);
	//	tx = cursor->x / CHUNK_SIZE_W;
	//	ty = cursor->y / CHUNK_SIZE_H;
	//	//else
	//		//Map[tx + (ty * MapH)].giveChange(cursor);
	//}


	//Add all new changes to the proper chunk that they happened in
	int x = 0,y = 0;
	int t = 0;
	while( AllChanges.size() ) {
		x = AllChanges.getByIndex(0)->x - 1 * BLOCK_TEXTURE_SIZE;
		y = (-1 * AllChanges.getByIndex(0)->y) - 1 * BLOCK_TEXTURE_SIZE;
		x /= CHUNK_SIZE_W;
		y /= CHUNK_SIZE_H;
		Map[x + (y * MapH)].AddChange(AllChanges.getByIndex(0));
		AllChanges.remove(0);
	}
	//Make any changes that are allowed
	for(int i = 9-1;i >= 0;i--) {
		if(CurrentLoc[i])
			CurrentLoc[i]->FinishAllChanges();
	}

	/*if(Priority != NULL) {
	Priority->FinishAllChanges();
	}*/
	return SUCCEED;
}

int const _World::ClearChanges() {
	while( AllChanges.size() ) {
		AllChanges.remove(0);
	}
	if(AllChanges.size() == 0) return SUCCEED;
	else return UNKNOWN;
}

// used for adding an NPC player in multiplayer
int _World::addNewPlayer(Entity* e)
{
	int index;
	// look for the first open slot in the Players array
	for(index = 0; index < MAX_PLAYERS && Players[index]; ++index)
	{
		if(Players[index]->playerIndex == e->playerIndex)
			return -1;
	}
	Players[index] = e;
	AllEntities->push(Players[index]);
	Players[index]->objectType = TYPE_ENTITY_PIECE;
	Players[index]->setEntityType(P_TYPE_PLAYER);

	// add new player to draw list
	Engine::Ins()->dlHandler.AddObject(e, DRAW_ENTITIES);
	for(int i = 0; i < Entity::e_TotalBPs; ++i)
		Engine::Ins()->dlHandler.AddObject(e->getBodyPart(i), DRAW_ENTITIES);

	return index;
}

void _World::addNewEntity(Entity* e)
{
	AllEntities->push(e);
	e->setEntityType(P_TYPE_ENEMY);

	// add entity to draw list
	for(int i = 0; i < Entity::e_TotalBPs; ++i)
		Engine::Ins()->dlHandler.AddObject(e->getBodyPart(i), DRAW_ENTITIES);
}

void _World::removePlayerEntities()
{
	for(int i = LOCAL_PLAYER+1; i < MAX_PLAYERS; ++i)
		removeEntity(i);
	GUI_Handler::get().setMessage("Now continuing in singleplayer game", GUI_Handler::CHATBOXOUTPUT);
}

void _World::removeEntity(short index)
{
	if(!Players[index])
		return;

	// remove all of the disconnected entity from the drawlist
	Entity* e = Players[index];
	for(int i = 0; i < Entity::e_TotalBPs; ++i)
		Engine::Ins()->dlHandler.RemoveObjectByRef(DRAW_ENTITIES, e->getBodyPart(i));
	Engine::Ins()->dlHandler.RemoveObjectByRef(DRAW_ENTITIES, e);

	// search Entities list for our disconnected entity
	/*for(int i = 0; i < AllEntities->size(); ++i){
		if(AllEntities->getByIndex(i) == e) {
			AllEntities->remove(i);
			break;
		}
	}*/

	char buffer[64];
	if(e->playerIndex >= 0 && e->playerIndex < MAX_PLAYERS)
		sprintf_s(buffer, 64, "Player %i has disconnected.", e->playerIndex+1);
	GUI_Handler::get().setMessage(buffer, GUI_Handler::CHATBOXOUTPUT);

	delete Players[index];
	Players[index] = NULL;
}

void _World::Update() {
	int R;
	// BNNC BY NEW NETWORK COMMENTS :

	R = ProcessChanges();
	R = PlayerUpdate();
	//r = ProcessEntityChanges();
	R = EnemyUpdate();
	R = ProjectileUpdate();
}
/////////////////
//UPDATES FOR ENTITIES
/////////////////
int const _World::PlayerUpdate() {
	
	MoveInfo	m;
	bool		momentumShifted = false;
	Vector2D	oldMomentum = Players[LOCAL_PLAYER]->getMomentum();
	bool		oldGravity  = Players[LOCAL_PLAYER]->bGravity;
	Vector<int> mq = Players[LOCAL_PLAYER]->MovementQueue;
	for(int i = 0; i < mq.size(); ++i)
		if(mq[i] != NONE){
			momentumShifted = true;
			break;
		}
	Players[LOCAL_PLAYER]->UpdateEntity(AllEntities);

	float px = Players[LOCAL_PLAYER]->getPos().x;
	float py = Players[LOCAL_PLAYER]->getPos().y;
	float pz = Players[LOCAL_PLAYER]->getPos().z;

	CleanLists();

	Result(ReloadChunks(px,py,pz));	

	//The following function takes player pos as a parameter to try and keep it modular
	GetCollisionList(px,py,BLOCK_COLLIDE_RADIUS);

	GetProjectileCollisionList(Players[LOCAL_PLAYER], &ProjectileCollideListSize,MAX_PROJECTILES,PROJECTILE_COLLIDE_RADIUS);

	Collide(Players[LOCAL_PLAYER], true);

	if(PlayerReset) {
		PlayerReset = false;
		return SUCCEED;
	}

	//==========UPDATE ANIMATION FOR LOCAL PLAYER
	XYZ* NewPlacements = new XYZ[Entity::e_TotalBPs];
	XYZ* NewRotations = new XYZ[Entity::e_TotalBPs];
	bool needtochange = animationEngine.Update(Players[LOCAL_PLAYER]->activeAnimations, Players[LOCAL_PLAYER]->currentFrames, NewPlacements, NewRotations, Players[LOCAL_PLAYER]->lastXDir);
	if(needtochange)
	{
		bool piecestoChange[Entity::e_TotalBPs];
		animationEngine.getActivePieces(piecestoChange);
		Players[LOCAL_PLAYER]->setPlacements(NewPlacements);
		Players[LOCAL_PLAYER]->setRotations(NewRotations);
	}
	playSoundsforPlayer(Players[LOCAL_PLAYER]->getActiveSounds());
	Players[LOCAL_PLAYER]->resetActiveSounds();
	
	// BNNC BY NEW NETWORK COMMENTS :
	//Process changes if able and needed
	//**
	//If there are changes in any of the loaded chunks, do them now.
	//If there are changes in unloaded chunks, save them to a change list
	// process them at next opportunity
	//**

	GetDrawList(px,py);

	// if multiplayer, send update info to other clients
	m.index = Players[LOCAL_PLAYER]->playerIndex;
	if(m.index >= 0 && m.index <= 4)
	{
		m.bGravity = Players[LOCAL_PLAYER]->bGravity;
		m.pos = Players[LOCAL_PLAYER]->getPos();
		m.p =   Players[LOCAL_PLAYER]->getMomentum();

		// if there's a big enough change in momentum, or gravity was changed, tell server
		if(m.bGravity != oldGravity || (fabs(m.p.x - oldMomentum.x) > 0.0f)
			|| (fabs(m.p.y - oldMomentum.y) > 0.0f))
			Engine::Ins()->game.m_network.MakeServerRequest(
			NETWORK_UPDATE_ENTITY, &m, sizeof(m));
	}

	// update other players
	Entity* e;
	for(int i = LOCAL_PLAYER+1; i < MAX_PLAYERS; ++i)
	{
		e = Players[i];

		// make sure that the entity is within loaded memory before attempting to update
		if(e != NULL && DetermineEntityChunkLoc(e->getPos().x, e->getPos().y, e->getPos().z))
		{
			e->UpdateSecondaryEntity();
			GetCollisionList(e->getPos().x, e->getPos().y,BLOCK_COLLIDE_RADIUS);
			GetProjectileCollisionList(e, &ProjectileCollideListSize,MAX_PROJECTILES,PROJECTILE_COLLIDE_RADIUS);
			Collide(e, true);
		}
	}
	AtmoSphere::get().update(Players[LOCAL_PLAYER]->getPos());
	delete NewPlacements;
	delete NewRotations;
	return SUCCEED;
}

int const _World::EnemyUpdate() {
	Entity * E = NULL;
	MoveInfo m;
	XYZ NewPlacements[Entity::e_TotalBPs];// = new XYZ[Entity::e_TotalBPs];
	XYZ NewRotations[Entity::e_TotalBPs];// = new XYZ[Entity::e_TotalBPs];
	for(int i = 1; i < AllEntities->size(); i++)
	{
		E = AllEntities->getByIndex(i);
		if(!E->bDummy)
		{
			bool oldGravity = E->bGravity;
			Vector2D oldMomentum = E->getMomentum();
		
			if((E != NULL && DetermineEntityChunkLoc(E->getPos().x, E->getPos().y, E->getPos().z) && E->getEntType() == P_TYPE_ENEMY))
			{
				E->UpdateEntity(AllEntities);		
				GetCollisionList(E->getPos().x,E->getPos().y,BLOCK_COLLIDE_RADIUS);
				GetProjectileCollisionList(E, &ProjectileCollideListSize,MAX_PROJECTILES,PROJECTILE_COLLIDE_RADIUS);
				Collide(E,0);
			}
			bool needtochange = animationEngine.Update(E->activeAnimations,E->currentFrames, NewPlacements, NewRotations, E->lastXDir);
			if(needtochange)
			{
				bool piecestoChange[Entity::e_TotalBPs];
				animationEngine.getActivePieces(piecestoChange);
				E->setPlacements(NewPlacements);
				E->setRotations(NewRotations);
			}
			playSoundsforPlayer(E->getActiveSounds());
			E->resetActiveSounds();

			// if multiplayer, send update info to other clients
			if(Engine::Ins()->game.m_network.isInitialized())
			{
				m.index = i;
				m.bGravity = E->bGravity;
				m.pos = E->getPos();
				m.p =   E->getMomentum();

				// if there's a big enough change in momentum, or gravity was changed, tell server
				if(m.bGravity != oldGravity || (fabs(m.p.x - oldMomentum.x) > 0.0f)
					|| (fabs(m.p.y - oldMomentum.y) > 0.0f))
					Engine::Ins()->game.m_network.MakeServerRequest(
					NETWORK_UPDATE_ENEMY, &m, sizeof(m));
			}
		}
		else
		{
			
			if(DetermineEntityChunkLoc(E->getPos().x, E->getPos().y, E->getPos().z) && E->playerIndex == -2)
			{
				E->UpdateSecondaryEntity();		
				GetCollisionList(E->getPos().x,E->getPos().y,BLOCK_COLLIDE_RADIUS);
				GetProjectileCollisionList(E, &ProjectileCollideListSize,MAX_PROJECTILES,PROJECTILE_COLLIDE_RADIUS);
				Collide(E,false);
			}
		}
	}
	//delete NewPlacements;
	//delete NewRotations;
	return NULL;
}

void _World::playSoundsforPlayer(bool* p_ActiveSounds)
{
	if(p_ActiveSounds[WAV_SWOOSH] == true) Engine::Ins()->sound.PlayCue("Swoosh");
	if(p_ActiveSounds[WAV_GUNSHOT] == true) Engine::Ins()->sound.PlayCue("Gunfire");
	if(p_ActiveSounds[WAV_FIREBALL] == true) Engine::Ins()->sound.PlayCue("Fireball");
	if(p_ActiveSounds[WAV_CRUMBLE] == true) Engine::Ins()->sound.PlayCue("Crumble");
	if(p_ActiveSounds[WAV_ERROR] == true) Engine::Ins()->sound.PlayCue("Error");
	if(p_ActiveSounds[WAV_PLAYERDEATH] == true) Engine::Ins()->sound.PlayCue("Human_Death");
	if(p_ActiveSounds[WAV_ZOMDEATH] == true) Engine::Ins()->sound.PlayCue("Zombie_Death");
	if(p_ActiveSounds[WAV_DRONEDEATH] == true) Engine::Ins()->sound.PlayCue("Drone Death");
	if(p_ActiveSounds[WAV_STIM] == true) Engine::Ins()->sound.PlayCue("Stimpack");
}


// The idea behind these functions
// Basically they find all the blocks near a position and add them to a list. Since each block_info knows a location I store them in the list one after another.
// So to itterate through the collision list all you need is one for loop with one itterator. The order in which it goes through blocks will not be consistent, but
// it makes itteration easy and fast. The other function for getDrawlist does the same thing but that doesn't affect collision.
// An example of calling GetDrawList(Player->getPos().x,Player->getPos().y);
//
// There is only one collision list that is shared by all objects. I have the memory managed among these arrays so that there is no allocation and deallocation. But you have to
// call GetCollisionList() every time you want a new list based on an objects position. GetDrawList() must also be called at every update with the players position.

//This is magic, but it still needed to make sure that the screen draws in a rectangle shape properly for now
#define MAGIC_HACK_MOD 2 // Can be undefined after the GetBlockList functions
// BlockListDecl
//These are declared as static to avoid continuously allocating and reallocating this stack memory
static int ChunkXPos,BlockXPos;
static int ChunkYPos,BlockYPos;
static int ChunkArrayPosition;
static int BlockArrayPosition;
//This class does the calculations in a reverse sprial order
static RWalkingCursor WlkCursor;
void _World::GetBlockList(Vector2D a_Pos,float Dist, Block_Info ** List,int * ListSize,int MaxSize) {
	(*ListSize) = 0;


	//Ctrl+f "BlockListDecl" to go to the declaration of this variable
	ChunkXPos = (int)(a_Pos.x / (CHUNK_SIZE_W));

	//Starts the block x position at the left side
	//Ctrl+f "BlockListDecl" to go to the declaration of this variable
	BlockXPos = (int)(a_Pos.x / BLOCK_TEXTURE_SIZE - (ChunkXPos * CHUNK_WIDTH) - Dist-MAGIC_HACK_MOD);

	// The y component is multiplied by -1 to convert it from world coordinates to array coordinates
	//Ctrl+f "BlockListDecl" to go to the declaration of this variable
	ChunkYPos = (int)(-1 * a_Pos.y / (CHUNK_SIZE_H));

	//Starts the block y position at the top. Dist is divided by two because our screen is rectangle
	//Ctrl+f "BlockListDecl" to go to the declaration of this variable
	BlockYPos = (int)(-1 * a_Pos.y / BLOCK_TEXTURE_SIZE - (ChunkYPos * CHUNK_WIDTH) - (int)(Dist/2));

	CheckListBoundaries(BlockXPos,ChunkXPos,BlockYPos,ChunkYPos);

	//Ctrl+f "BlockListDecl" to go to the declaration of this variable
	ChunkArrayPosition = ChunkXPos + (ChunkYPos * MapH);
	BlockArrayPosition = BlockXPos + (BlockYPos * CHUNK_HEIGHT);

	short End = (short)(Dist * 2);
	//ScXp = Start chunk X position
	short ScXp = ChunkXPos;
	//ScXp = Start block X position
	short SbXp = BlockXPos;

	Block * cursor;

	for(int yCount = (int)Dist-1;yCount >= 0;yCount--,BlockYPos++) {
		CheckListBoundaries(BlockXPos,ChunkXPos,BlockYPos,ChunkYPos);
		ChunkArrayPosition = ChunkXPos + (ChunkYPos * MapH);
		for(int xCount = End-1;xCount >= 0;xCount--,BlockXPos++) {
			if(CheckListBoundaries(BlockXPos,ChunkXPos,BlockYPos,ChunkYPos)) {
				ChunkArrayPosition = ChunkXPos + (ChunkYPos * MapH);
			}

			BlockArrayPosition = BlockXPos + (BlockYPos * CHUNK_HEIGHT);
#ifdef _DEBUG
			assert(Map[ChunkArrayPosition].Access() != NULL);
#else
			if(Map[ChunkArrayPosition].Access() == NULL) {
				if((*ListSize) + 1 < MaxSize)
					List[(*ListSize)+1]->clear();
				return;
			}
#endif
			cursor = Map[ChunkArrayPosition].Access();
#ifdef _DEBUG
			assert(cursor != NULL);
#else
			if(cursor == NULL) {
				if((*ListSize) + 1 < MaxSize)
					List[(*ListSize)+1]->clear();
				return;
			}
#endif
			//This code is to be used if we don't want air in our lists
			//&& cursor[BlockArrayPosition].GetType_s() > BLOCK_SOLIDS )
			if ( cursor[BlockArrayPosition].GetData_s() &&
				cursor[BlockArrayPosition].GetType_s() > BLOCK_DRAWABLES &&
				cursor[BlockArrayPosition].GetAssetID_s())
			{	
				if(BlockXPos == 0) {
					int x = 0;
					x = 1;
				}
				List[(*ListSize)]->Data = &cursor[BlockArrayPosition];
				List[(*ListSize)]->X = (BlockXPos + 1) * BLOCK_TEXTURE_SIZE + Map[ChunkArrayPosition].GetPosModX();
				List[(*ListSize)]->Y = -1 * ((BlockYPos + 1) * BLOCK_TEXTURE_SIZE + Map[ChunkArrayPosition].GetPosModY());

				(*ListSize)++;
				if((*ListSize) == MaxSize)
					break;
			}
		}
		if((*ListSize) == MaxSize)
			break;
		ChunkXPos = ScXp;
		BlockXPos = SbXp;
	}
	if((*ListSize) + 1 < MaxSize)
		List[(*ListSize)+1]->clear();
}

//Used specifically for drawing so Air blocks are excluded
void _World::GetSpiralBlockList(Vector2D a_Pos,float Dist, Block_Info ** List,int * ListSize,int MaxSize) {
	//A simple way of setting the list to empty. Since the data is being reused, it just pretends that the entire is empty
	(*ListSize) = 0;
	List[(*ListSize)]->clear();

	//Gets the current chunks x array position
	//Ctrl+f "BlockListDecl" to go to the declaration of this variable
	ChunkXPos = (int)(a_Pos.x / (CHUNK_SIZE_W));

	//Gets the current blocks x array position in the current chunk
	//Ctrl+f "BlockListDecl" to go to the declaration of this variable
	BlockXPos = (int)(a_Pos.x / BLOCK_TEXTURE_SIZE - (ChunkXPos * CHUNK_WIDTH));

	//Gets the current chunks y array position
	//Ctrl+f "BlockListDecl" to go to the declaration of this variable
	ChunkYPos = (int)(-1 * a_Pos.y / (CHUNK_SIZE_H));

	//Gets the current blocks y array position in the current chunk
	//Ctrl+f "BlockListDecl" to go to the declaration of this variable
	BlockYPos = (int)(-1 * a_Pos.y / BLOCK_TEXTURE_SIZE - (ChunkYPos * CHUNK_WIDTH));

	//Ctrl+f "BlockListDecl" to go to the declaration of this variable
	WlkCursor.Init(0,0,(int)(Dist)*2,(int)(Dist/2));

	int TCXP = 0;
	int TCYP = 0;
	int TBXP = 0;
	int TBYP = 0;

	Block * cursor;

	while(!WlkCursor.Stop) {
		//Does the first step
		WlkCursor.Walk();
		TBXP = BlockXPos + WlkCursor.x;
		TBYP = BlockYPos + WlkCursor.y;

		TCXP = ChunkXPos;
		TCYP = ChunkYPos;

		//Modifies the numbers to keep them within the array bounds
		CheckListBoundaries(TBXP,TCXP,TBYP,TCYP);

		//Calculates the array positions once instead of each time its needed
		ChunkArrayPosition = TCXP + (TCYP * MapH);
		BlockArrayPosition = TBXP + (TBYP * CHUNK_HEIGHT);

		

#ifdef _DEBUG
		assert(Map[ChunkArrayPosition].Access() != NULL);
#else
		if(Map[ChunkArrayPosition].Access() == NULL) {
			if((*ListSize) + 1 < MaxSize)
				List[(*ListSize)+1]->clear();
			return;
		}
#endif
		//Gets the current chunk
		cursor = Map[ChunkArrayPosition].Access();
			
#ifdef _DEBUG
		assert(cursor != NULL);
#else
		if(cursor == NULL) {
			if((*ListSize) + 1 < MaxSize)
				List[(*ListSize)+1]->clear();
			return;
		}
#endif

		//If the current spiraling position is to high or to low, its ignored.
		//This creates the rectangle draw list instead of a square
		if( WlkCursor.InBounds() &&
			//This makes sure that the block is actually valid
			cursor[BlockArrayPosition].GetData_s() &&
			//This excludes all non drawable blocks
			cursor[BlockArrayPosition].GetType_s() > BLOCK_DRAWABLES &&
			cursor[BlockArrayPosition].GetAssetID_s()) {
				//Add the block, and the block position for the drawing equation
				List[(*ListSize)]->Data = &cursor[BlockArrayPosition];
				List[(*ListSize)]->X = (TBXP + 1) * BLOCK_TEXTURE_SIZE + Map[ChunkArrayPosition].GetPosModX();
				List[(*ListSize)]->Y = -1 * ((TBYP + 1) * BLOCK_TEXTURE_SIZE + Map[ChunkArrayPosition].GetPosModY());
				(*ListSize)++;
				//Stops the loop if it tries to add to many blocks
				if((*ListSize) >= MaxSize)
					break;
		}
	}
	if((*ListSize) + 1 < MaxSize)
		List[(*ListSize)+1]->clear();
}
#undef MAGIC_HACK_MOD

//cx = current world x position
//cy = current world y position
void _World::GetDrawList(float const & cx,float const & cy) {
	GetSpiralBlockList(Vector2D(cx,cy),DRAWLIST_RADIUS,DrawList,&DrawListSize,MAX_LIST_SIZE);
}

//cx = current world x position
//cy = current world y position
void _World::GetCollisionList(float const & cx,float const & cy,int const & a_Dist) {
	GetBlockList(Vector2D(cx,cy),(float)a_Dist,CollideList,&CollideListSize,MAX_LIST_SIZE);
}

//cx = current world x position
//cy = current world y position
int const _World::GetProjectileCollisionList(Entity* E, int * ListSize,int const MaxSize,int const & a_Dist) {
	//Clears out the list
	(*ListSize) = 0;
	ProjectileCollideList[(*ListSize)] = NULL;

	//Gets the list to add
	void * List = Factory::get().GetProjectileList();
	if(!List) return FAIL_MEMORY_COPY;

	Item_Projectile*	canCollide;

	//Adds all relevant projectiles
	for(int i = 0; i < (projList(List))->size(); i++) {
		canCollide = &(projList(List))->get(i);
		if((abs(canCollide->getPos().x - E->getPos().x) + abs(canCollide->getPos().y - E->getPos().y)) < PROJECTILE_COLLIDE_RADIUS) 
		{
			ProjectileCollideList[(*ListSize)] = canCollide;
			(*ListSize)++;
		}
		//Size limiter
		if((*ListSize) == MaxSize)
			break;
	}

	//Sets the last projectile to null if the list isn't full
	if((*ListSize) + 1 < MaxSize)
		ProjectileCollideList[(*ListSize)+1] = NULL;
	return SUCCEED;
}
#undef projList

bool _World::CheckListBoundaries(int & bx,int & cx,int & by,int & cy) {
	bool Changed = false;
	//** X BOUNDARY CODE
	// Checks the block x position
	//If it goes out of a chunks bounds, it moves the chunk x position accordingly,
	// If it cannot move any further it sets the positions appropriately.
	while ( bx > CHUNK_WIDTH-1 ) {
		bx -= CHUNK_WIDTH;
		if(cx+1 > MapW-1) {
			bx = CHUNK_HEIGHT-1;
		}
		else {
			cx++;
		}
		Changed = true;
	}

	while ( bx < 0 ) {
		bx += CHUNK_WIDTH;
		if(cx-1 < 0) {
			bx = 0;
		}
		else {
			cx--;
		}
		Changed = true;
	}
	//**

	//** X chunk Position FAILSAFE CODE
	if(cx < 0) {
		cx = 0;
	}
	else if (cx > MapW-1) {
		cx = MapW-1;
	}
	//**

	//** Y BOUNDARY CODE
	// Checks the block y position
	// Same as the above code
	while ( by > CHUNK_HEIGHT-1 ) {
		by -= CHUNK_HEIGHT;
		if(cy+1 > MapH-1) {
			by = CHUNK_HEIGHT-1;
		}
		else {
			cy++;
		}
		Changed = true;
	}

	while ( by < 0 ) {
		by += CHUNK_HEIGHT;
		if(cy-1 < 0) {
			by = 0;
		}
		else {
			cy--;
		}
		Changed = true;
	}
	//**

	//** Y chunk Position FAILSAFE CODE
	if(cy < 0) {
		cy = 0;
	}
	else if (cy > MapH-1) {
		cy = MapH-1;
	}
	//** 
	return Changed;
}

int const _World::Regenerate() {
	
	////int Density = 1;//(rand() % 3) + 1;
	//int TotalCount = AllEntities->size()-1;

	for(int i = 9-1;i >= 0;--i) {
		if(CurrentLoc[i]) {
			CurrentLoc[i]->Unload();
			CurrentLoc[i]->Saved();
		}
		CurrentLoc[i] = NULL;
	}
	for(int y = 0;y < MapH;y++) {
		for(int x = 0;x < MapW;x++) {
			if(Map[x + y * MapH].IsLoaded()) {
				Map[x + y * MapH].Unload();
			}
		}
	}

	GENERATOR::Ins()->ReInit();

	ResetSaveFile();
	
	SetupEntities(Density);
	//ResetEntities();
	////////
	//////////Setup the 9 chunks to be loaded first
	for(int y = 0;y < MapH;y++) {
		for(int x = 0;x < MapW;x++) {
			Map[x + y * MapH].Load(BufferSpace[0]);
			CurrentLoc[0] = &Map[x + y * MapH];
			SetAssets(CurrentLoc[0]);
			CurrentLoc[0]->Unload();
		}
	}

	float H,W,X,Y;
	//!//Changed pos to the end
	X = (float)(BLOCK_TEXTURE_SIZE * 2);
	//X = (float)(MapW-1) * CHUNK_WIDTH * BLOCK_TEXTURE_SIZE - BLOCK_TEXTURE_SIZE;
	Y = (float)FindGroundLevel((int)X,false);
	Players[LOCAL_PLAYER]->CalcBBDimensions(W,H);
	//SetupEntities(3);
	Players[LOCAL_PLAYER]->SetPos(X, -1 * (Y - 30), 0.0f);
	Vector3D P = Players[LOCAL_PLAYER]->getPos();
	ForceReload(P.x,P.y,P.z,false);
	ClearChanges();	

	return SUCCEED;
}

int const _World::ResetPlayer(Entity * E, bool Dead) {
	PlayerReset = true;
	//Resets his position to the beginning of the world
	float H,W;
	E->CalcBBDimensions(H,W);
	SaveCurrent();
	//!//Changed pos to the end
	float x = (float)(BLOCK_TEXTURE_SIZE);
	//float x = (float)((MapW-1) * CHUNK_WIDTH * BLOCK_TEXTURE_SIZE - BLOCK_TEXTURE_SIZE);
	float y = (float)FindGroundLevel((int)x,false) * -1.0f + (int)H;
	E->SetPos((x + (int)W * 3),y,0);
	CleanLists();
	ForceReload(E->getPos().x,E->getPos().y,0,true);
	if(Dead) {
		E->resetPlayerfromDeath();
	}
	return SUCCEED;
}

int const _World::Save() {
	// The path to the save file
	// The map size x then y
	// The path to the biome text files
	// The list of all the text files(+paths) and their locations in the map
	char * buffer = new char[24];
	sprintf_s(buffer,24,"%sSave.txt",FullPath);
	CreateFile(buffer,NULL,NULL,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);

	std::ofstream out;
	out.open(buffer,std::ios::out);

	//TODO BY : BIOME_LIST RENAME
	/*out << FullPath << '\n' << MapW << ' ' << MapH << '\n' << BiomeListPath << '\n';*/

	for(int y = MapH - 1,x = MapW - 1;y >= 0;x--) {
		out << Map[x + (y * MapH)].getFileName() << '\n';
		out << x << " " << y << '\n';

		if( x <= 0 ) {
			x = MapW;
			--y;
		}
	}

	out.close();
	delete buffer;

	for(int i = 9-1;i >= 0;--i) {
		if(CurrentLoc[i]) {
			CurrentLoc[i]->Save();
			CurrentLoc[i]->Saved();
		}
	}

	return SUCCEED;
}

int const _World::SaveCurrent() {
	for(int i = 9-1;i >= 0;--i) {
		if( CurrentLoc[i] && CurrentLoc[i]->NeedSave() ) {
			CurrentLoc[i]->Save();
			CurrentLoc[i]->Saved();
		}
	}
	return SUCCEED;
}

// creates an array of strings, with each string holding one of the save files such as "0 0.txt"
char** _World::ConcatenateSaveInfo()
{
	char** ret = NULL;
	int numFiles = MapH*MapW;
	ret = new char*[numFiles];
	ifstream infile;

	char* buffer = NULL;
	int bufferSize = 0;
	int i = 0;
	for(int y = 0; y < MapH; ++y)
	{
		for(int x = 0; x < MapW; ++x, ++i)
		{
			char filename[64];
			sprintf_s(filename, 64, "%s%i %i.txt", FullPath, y, x);
			infile.open(filename);
			if(infile.is_open())
			{
				// the first number in the file should be the number of characters within the file
				infile >> bufferSize;
				bufferSize++;

				// there's most likely a better way to do this, but I need to reset the input buffer and I don't know of a better way
				infile.close();
				infile.open(filename);

				// allocate memory for the entire text file stored as a string plus room for a null terminator
				buffer = new char[bufferSize];
				infile.getline(buffer, bufferSize);
				buffer[bufferSize-1] = '\0'; 
				ret[i] = buffer;
			}
			infile.close();
		}
	}

	return ret;
}

int _World::DeleteWorld(char* WorldDirectory)
{
	char * Buffer = NULL;
	for(int y = MapH - 1,x = MapW - 1;y >= 0;x--) {
		Buffer = new char[128];
		sprintf_s(Buffer,128,"%s%i %i.txt",FullPath,x,y);
		remove(Buffer);

		if( x <= 0 ) {
			x = MapW;
			--y;
		}
	}
	//Removes the save file that holds extra world information
	//Temp code
	if(MapH && MapW) {
		sprintf_s(Buffer,128,"%sSave.txt",FullPath);
		remove(Buffer);
	}
	return SUCCEED;
}

int const _World::CleanUp() {
	if(Map) {
		Save();
		delete [] Map;
		Map = NULL;
	}
	if(Players[LOCAL_PLAYER]) { delete Players[LOCAL_PLAYER]; Players[LOCAL_PLAYER] = NULL; }

	if(BufferSpace) {
		for(int i = 9 - 1;i >= 0;--i) {
			delete [] BufferSpace[i];
		}
	}

	/*if(BiomeListPath) { delete BiomeListPath; BiomeListPath = NULL;}

	BiomeList.clear();*/

	Engine::Ins()->dlHandler.RemoveWorldBlocks(0,DrawListSize);

	// BYC : Yorks concerns
	//Why is this being called
	//DeleteWorld(BASE_MAP_NAME);

	if(rawMultiSaveData)
	{
		for(int i = 0; i < numFiles; ++i)
			delete rawMultiSaveData[i];
		delete rawMultiSaveData;
		rawMultiSaveData = NULL;
	}

	if(FullPath) { delete FullPath; FullPath = NULL; }

	return SUCCEED;
}

int const _World::CleanLists() {
	for(int i = DrawListSize-1;i >= 0;i--) {
		DrawList[i]->clear();
	}
	DrawListSize = 0;

	for(int j = CollideListSize-1;j >= 0;j--) {
		CollideList[j]->clear();
	}
	CollideListSize = 0;

	return SUCCEED;
}

//Functions to make later

//Determine if an action should be done or not
/*
loop through all actions
Convert location to block and chunk positions
Check if the chunk the block to modify is in is loaded in memory
If yes,
Do action (defined below)
else go to top of loop
*/

//Do action to block
/*
switch(Flagreceived)
{
add block

(Questionable about how this will work because damage is not yet in the game)
damage block

remove block

convert block
}
*/ 